using System;
using System.Collections;
using System.IO;

using Qiang.Topology.Geometries;

namespace Qiang.Topology.IO
{
    /// <summary>
    ///  A class for writing shapefiles data.
    /// </summary>
    public class ShapeFileWriter : ShapeWriter
    {
         /// <summary>
        /// Default empty constructor
        /// </summary>
        public ShapeFileWriter() : base() { }

        /// <summary>
        /// Writes an homogeneus <c>GometryCollection</c> into a binary shapefile.
        /// </summary>
        /// <param name="geometries">If geometries are not of the same type throws <c>System.ArgumentException</c>.</param>
        /// <param name="shapepath">Path of the shapefile to create: will be created a new file using the given path.
        /// if file exists throws <c>System.IO.IOException</c>.</param>        
        public virtual void Write(GeometryCollection geometries, string shapepath)
        {            
            using (Stream stream = new FileStream(shapepath, FileMode.CreateNew, FileAccess.Write, FileShare.None))
            {
                Write(geometries, stream);
            }
        }

        /// <summary>
        /// Writes an homogeneus <c>GometryCollection</c> into a binary stream.
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="stream"></param>
        public virtual void Write(GeometryCollection geometries, Stream stream)
        {
            if (!geometries.IsHomogeneous)
                throw new ArgumentException("Shapefile does contain only geometries of th esame type!");           
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                WriteHeaderFile(geometries, writer);
                foreach (Geometry geometry in geometries.Geometries)
                     Write(geometry, writer);
            }            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="geometry"></param>
        /// <param name="writer"></param>
        protected virtual void Write(Geometry geometry, BinaryWriter writer)
        {
            WriteFeatureHeader(writer);             // FIXME: CALL TO base.Write is clearly an error!   
            if (geometry is Point)                
                base.Write(geometry as Point, writer);
            else if (geometry is LineString)
                base.Write(geometry as LineString, writer);
            else if (geometry is Polygon)
                base.Write(geometry as Polygon, writer);
            else if (geometry is MultiPoint)
                base.Write(geometry as MultiPoint, writer);
            else if (geometry is MultiLineString)
                base.Write(geometry as MultiLineString, writer);
            else if (geometry is MultiPolygon)
                base.Write(geometry as MultiPolygon, writer);
            else throw new NotSupportedException("Unsupported Geometry implementation:" + geometry.GetType());
        }        

        /// <summary>
        /// 
        /// </summary>
        /// <param name="geometry"></param>
        /// <returns></returns>
        protected virtual ShapeGeometryTypes GetShapeType(IGeometry geometry)
        {
            if (geometry is Point)
                return ShapeGeometryTypes.Point;
            else if (geometry is LinearRing)
                return ShapeGeometryTypes.LineString;
            else if (geometry is LineString)
                return ShapeGeometryTypes.LineString;
            else if (geometry is Polygon)
                return ShapeGeometryTypes.Polygon;
            else if (geometry is MultiPoint)
                return ShapeGeometryTypes.MultiPoint;
            else if (geometry is MultiLineString)
                return ShapeGeometryTypes.LineString;
            else if (geometry is MultiPolygon)
                return ShapeGeometryTypes.Polygon;
            else throw new NotSupportedException("Unsupported Geometry implementation:" + geometry.GetType());            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="writer"></param>
        protected virtual void WriteHeaderFile(GeometryCollection geometries, BinaryWriter writer)
        {
            // Write stub value for FileCode (big endian)
            writer.Write((int)0);
            // Write stub values for unused (big endian)
            writer.Write((int)0);
            writer.Write((int)0);
            writer.Write((int)0);
            writer.Write((int)0);
            writer.Write((int)0);
            // Write stub value for Length (big endian)
            writer.Write((int)0);
            // Write version
            writer.Write((int)1000);
            // Write ShapeTipe
            writer.Write((int)GetShapeType(geometries.Geometries[0]));
            // Write values for boundingbox      
            IEnvelope envelope = geometries.EnvelopeInternal;
            writer.Write((double)envelope.MinX);
            writer.Write((double)envelope.MinY);
            writer.Write((double)envelope.MaxX);
            writer.Write((double)envelope.MaxY);
            writer.Write((double)0);
            writer.Write((double)0);
            writer.Write((double)0);
            writer.Write((double)0);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="geometry"></param>
        /// <param name="writer"></param>
        protected virtual void WriteFeatureHeader(BinaryWriter writer)
        {
            // Write stub value for record number (big endian)
            writer.Write((int)0);
            // Write stub value for content length (big endian)
            writer.Write((int)0);
        }
    }
}
